#pragma once

#include<iostream>
#include<string>
#include<memory>
#include<sys/select.h>
#include"Log.hpp"
#include"Socket.hpp"

Log lg;
using namespace Network;
using namespace std;

const static int gdefaultport=8888;
const static int gbacklog=8;

const static int num=sizeof(fd_set)*8; 
class SelectServer
{
    private:
        void HandlerEvent(fd_set& rfds)
        {
            if(FD_ISSET(_listensock->GetSockfd(),&rfds))
            {
                lg.LogMessage(Info,"get a new link\n");
                //获取连接
                string clientip;
                uint16_t clientport;
                //不会阻塞！！因为select已经检测到了listensock已经就绪了
                Socket* sock=_listensock->AcceptConnection(&clientip,&clientport);
                if(!sock)
                {
                    lg.LogMessage(Error,"accept error\n");
                    return;
                }
                lg.LogMessage(Info,"get a client, client info is# %s:%d,fd:%d\n",clientip.c_str(),clientport,sock->GetSockfd());

                //这里已经获取连接成功了，接下来怎么办呢？
                //read？write？绝对不能！！！read底层数据是否就绪是不确定的！谁最清楚fd上面是否有读取事件呢？select！
                //新连接fd到来的时候，要把新的fd，想办法交给select来管理——只需要将新的fd添加到_rfds_arry中即可
            }
        }
    public:
        SelectServer(int port=gdefaultport):_port(port),_listensock(new TcpSocket()),_isrunning(false)
        {}
        void InitServer()
        {
            _listensock->BuildlistenSocketMethod(_port,gbacklog);
            for(int i=0;i<num;i++)
            {
                _rfds_arry[i]=nullptr;
            }
        }
        void Loop()
        {
            _isrunning=true;
            while(_isrunning)
            {
                //我们能不崩直接accept新连接呢，不能！所有的fd，都要交给select来管理，listensock上面新连接，
                //相当于读事件，有新连接，就等价于有新数据到来
                //
                //首先不能直接accept，而是将listensock交给select。因为只有select有资格知道有没有IO事件就绪
                //
                //故意放在循环内部
                fd_set rfds;
                FD_ZERO(&rfds);
                FD_SET(_listensock->GetSockfd(),&rfds);//

                //遍历数组，1.找到最大的fd 2.合法的fd添加到rfds集合中

                //定义时间
                struct timeval timeout={5,0};
                int n=select(_listensock->GetSockfd()+1,&rfds,nullptr,nullptr,&timeout);
                switch(n)
                {
                    case 0:
                        lg.LogMessage(Info,"select timeout....,last time:%u.%u\n",timeout.tv_sec,timeout.tv_usec);
                        break;
                    case -1:
                        lg.LogMessage(Error,"select error!!!\n");
                        break;
                    default:
                        //正常的就绪fd
                        lg.LogMessage(Info,"select success,begin event handle,last time:%u.%u\n",timeout.tv_sec,timeout.tv_usec);
                        HandlerEvent(rfds);
                        break;
                }
            }
            _isrunning=false;
        }
        void Stop()
        {
            _isrunning=false;
        }
        ~SelectServer()
        {}

    private:
        unique_ptr<Socket> _listensock;
        int _port;
        bool _isrunning;
        Socket* _rfds_arry[num];
};
